<template>
  <div class="attachments-uploader">
    <!-- 上传块（拖拽/点击），不自动上传 -->
    <el-upload
      ref="uploadRef"
      class="upload-block"
      drag
      :multiple="multiple"
      :auto-upload="false"
      :file-list="mergedFileList"
      :on-remove="onUploadRemove"
      :on-change="onUploadChange"
      :before-upload="beforeUpload"
      :http-request="noop"
      :on-preview="onPreview"
      :accept="accept"
      :list-type="listType"
    >
      <el-icon><UploadFilled /></el-icon>
      <div class="el-upload__text">将文件拖拽到此处，或 <em>点击上传</em></div>
      <template #tip>
        <div class="el-upload__tip">
          支持图片、PDF、Office、ZIP 等；单个文件建议 &lt; {{ maxSizeMB }}MB
        </div>
      </template>
    </el-upload>

    <!-- 选择文件夹（可配置开关） -->
    <div v-if="directory" class="mt8">
      <el-button size="small" @click="pickDirectory">选择文件夹</el-button>
      <input
        ref="dirPickerRef"
        type="file"
        style="display: none"
        webkitdirectory
        directory
        multiple
        @change="onDirectoryPicked"
      />
      <div class="text-12 text-gray mt4">
        文件夹上传将保留相对路径（relativePath）
      </div>
    </div>

    <!-- 已有附件列表（编辑态） -->
    <el-table
      v-if="showTable && serverRows.length"
      :data="serverRows"
      size="small"
      class="mt8"
    >
      <el-table-column prop="fileName" label="文件名" min-width="220" />
      <el-table-column prop="relativePath" label="相对路径" min-width="200" />
      <el-table-column prop="fileSize" label="大小(B)" width="120" />
      <el-table-column label="操作" width="160">
        <template #default="scope">
          <el-button link type="primary" @click="previewAttachment(scope.row)"
            >预览/下载</el-button
          >
          <el-button link type="danger" @click="removeAttachment(scope.row)"
            >删除</el-button
          >
        </template>
      </el-table-column>
    </el-table>
  </div>
</template>

<script setup>
import { ref, computed, watch, getCurrentInstance } from "vue";
import { UploadFilled } from "@element-plus/icons-vue";

const uploadRef = ref();
const dirPickerRef = ref();

// 服务器已有（表格 + el-upload 回显）
const serverRows = ref([]); // 原始行（表格）
const serverFileList = ref([]); // 映射成 el-upload 的 file 结构

// 本次新增、待上传
const pendingFileList = ref([]); // el-upload 的本地文件对象（含 raw）
// 合并给 el-upload
const mergedFileList = computed(() => [
  ...serverFileList.value,
  ...pendingFileList.value,
]);

/**
 * 可复用附件上传组件
 * - 不直接发请求，依赖父组件传入 listFn / uploadFn / deleteFn
 * - 支持文件夹选择并保留 webkitRelativePath -> relativePath
 * - 提供 expose 方法：reset/refresh/uploadAll/getPendingFiles
 */
const props = defineProps({
  /** 业务实体 ID（有则自动回显附件，没有则只收集待上传） */
  entityId: { type: [String, Number], default: null },

  /** API：查询已有关联附件 listFn(entityId) -> { rows:[{id,fileName,fileUrl,fileSize,relativePath}] } | rows[] */
  listFn: { type: Function, required: true },

  /** API：上传 uploadFn(entityId, FormData(files[], relativePaths[])) */
  uploadFn: { type: Function, required: true },

  /** API：删除 deleteFn(attachmentId) */
  deleteFn: { type: Function, required: true },

  /** 可选外观/限制 */
  accept: {
    type: String,
    default:
      ".png,.jpg,.jpeg,.webp,.gif,.pdf,.doc,.docx,.xls,.xlsx,.ppt,.pptx,.txt,.zip,.rar",
  },
  maxSizeMB: { type: Number, default: 100 },
  listType: { type: String, default: "picture-card" }, // 'text' | 'picture-card'
  multiple: { type: Boolean, default: true },
  directory: { type: Boolean, default: true },
  showTable: { type: Boolean, default: true },
  autoFetch: { type: Boolean, default: true },
  clearOnNull: { type: Boolean, default: true },
});

const emit = defineEmits(["change", "uploaded", "removed", "error", "ready"]);

 

/** 拉取并回显服务端附件 */
async function fetchServerFiles(id = props.entityId) {
  if (!id) {
    serverRows.value = []
    serverFileList.value = []
    return
  }
  try {
    const resp = await props.listFn(id)
    const list =
      Array.isArray(resp) ? resp :
      (resp?.rows ?? resp?.data ?? [])

    // 表格数据
    serverRows.value = list

    // el-upload 回显数据
    serverFileList.value = list.map(a => ({
      name: a.fileName,
      url: makeAbsUrl(a.fileUrl), // 你已有的工具：补 BASE_API 前缀
      status: 'success',
      size: a.fileSize,
      uid: `server_${a.id}`,      // 用于定位/删除
      id: a.id,
      isServer: true,
      relativePath: a.relativePath || ''
    }))

    emit('ready', { count: serverRows.value.length })
  } catch (e) {
    // 失败兜底
    serverRows.value = []
    serverFileList.value = []
    emit('error', e)
  }
}

/** 对外：刷新回显 */
// async function refresh() {
//   try {
//     const resp = await props.listFn(props.entityId);
//     const list = resp?.rows || resp?.data || resp || [];
//     serverRows.value = list;
//     serverFileList.value = list.map((a) => ({
//       name: a.fileName,
//       url: makeAbsUrl(a.fileUrl),
//       status: "success",
//       size: a.fileSize,
//       uid: `server_${a.id}`,
//       id: a.id,
//       isServer: true,
//       relativePath: a.relativePath || "",
//     }));
//     emit("ready", { count: serverRows.value.length });
//   } catch (e) {
//     emit("error", e);
//   }
// }


// 小工具：把后端响应统一成数组
const coerceRows = (resp) =>
  Array.isArray(resp) ? resp : (resp?.rows ?? resp?.data ?? [])

// 小工具：服务端附件 -> el-upload 文件结构
const toUploadFile = (a) => ({
  name: a.fileName,
  url: makeAbsUrl(a.fileUrl),
  status: 'success',
  size: a.fileSize,
  uid: `server_${a.id}`,
  id: a.id,
  isServer: true,
  relativePath: a.relativePath || '',
})

// 竞态保护：如果 entityId 变化很快，忽略落后的请求
let fetchToken = 0

async function loadServerFiles(id = props.entityId) {
  if (!id) { reset(); return }
  const token = ++fetchToken
  try {
    const resp = await props.listFn(id)
    if (token !== fetchToken) return  // 已经过期，丢弃

    const list = coerceRows(resp)
    serverRows.value = list
    serverFileList.value = list.map(toUploadFile)
    emit('ready', { count: list.length })
  } catch (e) {
    reset()
    emit('error', e)
  }
}

// 保留你对外的名字
const refresh = loadServerFiles

/** 对外：仅清空“待上传”与回显 */
function reset() {
  serverRows.value = [];
  serverFileList.value = [];
  pendingFileList.value = [];
}

/** 对外：获取待上传文件 */
function getPendingFiles() {
  return pendingFileList.value.slice();
}

/** 对外：执行上传（需传入 entityId；常见做法是父组件拿到新增后的 id 再调） */
async function uploadAll(entityIdArg) {
  const targetId = entityIdArg ?? props.entityId;
  if (!targetId) return { ok: true, uploaded: 0 };

  // 没有待上传则直接返回
  if (!pendingFileList.value.length) return { ok: true, uploaded: 0 };

  const fd = new FormData();
  let count = 0;
  for (const f of pendingFileList.value) {
    const raw = f.raw || f;
    if (!beforeUpload(raw)) continue;
    fd.append("files", raw);
    const relative = f.relativePath || raw.webkitRelativePath || "";
    fd.append("relativePaths", relative);
    count++;
  }
  if (count === 0) return { ok: true, uploaded: 0 };

  await props.uploadFn(targetId, fd);
  pendingFileList.value = [];
  await refresh();
  emit("uploaded", { count });
  return { ok: true, uploaded: count };
}

/** —— el-upload 相关 —— */
const noop = () => {};

function onUploadChange(_uploadFile, uploadFiles) {
  pendingFileList.value = (uploadFiles || []).filter((f) => !f.isServer);
  emit("change", { pending: pendingFileList.value.length });
}

async function onUploadRemove(file, uploadFiles) {
  if (file.isServer) {
    await props.deleteFn(file.id);
    serverFileList.value = serverFileList.value.filter(
      (f) => f.uid !== file.uid
    );
    serverRows.value = serverRows.value.filter(
      (x) => `server_${x.id}` !== file.uid
    );
    emit("removed", { id: file.id, server: true });
  } else {
    pendingFileList.value = pendingFileList.value.filter(
      (f) => f.uid !== file.uid
    );
    emit("removed", { uid: file.uid, server: false });
  }
}

function beforeUpload(file) {
  const isLt = file.size / 1024 / 1024 < props.maxSizeMB;
  if (!isLt) {
    getCurrentInstance()?.proxy?.$modal?.msgError?.(
      `文件 ${file.name} 超过 ${props.maxSizeMB}MB`
    );
    return false;
  }
  return true;
}

function onPreview(file) {
  const url = file.url || file.raw?.url;
  if (!url) return;
  window.open(url, "_blank");
}

function pickDirectory() {
  dirPickerRef.value && dirPickerRef.value.click();
}

function onDirectoryPicked(e) {
  const files = Array.from(e.target.files || []);
  const toPush = files.map((raw, idx) => ({
    name: raw.name,
    status: "ready",
    size: raw.size,
    raw,
    uid: `${Date.now()}_${idx}`,
    relativePath: raw.webkitRelativePath || "",
  }));
  pendingFileList.value = pendingFileList.value.concat(toPush);
  e.target.value = "";
  emit("change", { pending: pendingFileList.value.length });
}

/** 工具：相对路径转绝对 URL（兼容 RuoYi 的 BASE_API 前缀） */
function makeAbsUrl(path) {
  if (!path) return "";
  if (/^https?:\/\//i.test(path)) return path;
  const BASE =
    import.meta.env?.VITE_APP_BASE_API || process.env?.VUE_APP_BASE_API || "";
  return `${BASE}${path}`;
}

/** 表格操作（预览/删除已有） */
function previewAttachment(row) {
  window.open(makeAbsUrl(row.fileUrl), "_blank");
}
async function removeAttachment(row) {
  await props.deleteFn(row.id);
  await refresh();
  emit("removed", { id: row.id, server: true });
}

watch(
  () => props.entityId,
  async (id) => {
    if (!id) {
      if (props.clearOnNull) reset(); // 清空待上传 + 服务器文件
      return;
    }
    if (props.autoFetch) await fetchServerFiles(id); // 回显
  },
  { immediate: true }
);

watch(
  () => props.entityId,
  async (id) => {
    if (id) await refresh();
  },
  { immediate: true }
);

defineExpose({ reset, refresh, uploadAll, getPendingFiles });
</script>

<style scoped>
.mt4 {
  margin-top: 4px;
}
.mt8 {
  margin-top: 8px;
}
.text-12 {
  font-size: 12px;
}
.text-gray {
  color: #909399;
}
.upload-block :deep(.el-upload) {
  width: 100%;
}
</style>
